package com.yk.dataGatherer.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.yk.api.dataGatherer.dto.*;
import com.yk.api.dataGatherer.vo.FieldsVo;
import com.yk.api.system.dto.DeviceDTO;
import com.yk.api.system.dto.VariableDTO;
import com.yk.api.system.model.DeviceFeignService;
import com.yk.api.system.model.VariableFeignService;
import com.yk.common.core.constant.CacheConstants;
import com.yk.common.core.constant.NumberConstant;
import com.yk.common.core.constant.TdEngIneConstants;
import com.yk.common.core.domain.BasePageQuery;
import com.yk.common.core.domain.LoginUser;
import com.yk.common.core.domain.PageResult;
import com.yk.common.core.domain.Result;
import com.yk.common.core.enums.DataTypeEnum;
import com.yk.common.core.utils.LoginHelper;
import com.yk.common.core.utils.StringUtils;
import com.yk.common.excel.utils.ExcelUtil;
import com.yk.common.log.annotation.Log;
import com.yk.common.log.constant.LogConstants;
import com.yk.common.log.enums.BusinessType;
import com.yk.common.redis.service.RedisService;
import com.yk.dataGatherer.service.TdEngineService;
import com.yk.dataGatherer.vo.LastDataVO;
import com.yk.dataGatherer.vo.VariableDataVO;
import com.yk.dataGatherer.vo.VariableVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 设备数据 yk-dataGatherer
 *
 * @author lmx
 * @date 2023/10/27 15:43
 */
@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/dataOperation")
@Api(tags = "设备数据")
public class TdEngineController {

    private final TdEngineService tdEngineService;
    private final RedisService redisService;
    private final VariableFeignService variableFeignService;
    private final DeviceFeignService deviceFeignService;
    @Value("${spring.datasource.dynamic.datasource.master.dbName}")
    private String masterDataBaseName;

    @PostMapping("/createSTb")
    @ApiOperation("创建超级表")
    public Result<?> createSuperTable(@RequestBody SuperTableDTO superTableDto) {
        if (Objects.isNull(superTableDto) || CollUtil.isEmpty(superTableDto.getPtFields())){
            return Result.fail("超级表信息不能为空");
        }
        // 普通变量
        List<Fields> ptFields = superTableDto.getPtFields().stream().filter(Fields::getStorage)
                .collect(Collectors.toList());
        // 差值变量
        if (CollUtil.isNotEmpty(superTableDto.getCzFields())){
            List<Fields> czFields = superTableDto.getCzFields().stream().filter(Fields::getStorage)
                    .collect(Collectors.toList());
            ptFields.addAll(czFields);
        }
        // 内部变量
        if (CollUtil.isNotEmpty(superTableDto.getNbFields())){
            List<Fields> nbFields = superTableDto.getNbFields().stream().filter(Fields::getStorage)
                    .collect(Collectors.toList());
            ptFields.addAll(nbFields);
        }
        // 表字段只有TS时不进行创建，不然会创建报错
        if (ptFields.size() == NumberConstant.ONE) {
            return Result.ok();
        }
        // 标签字段对象集合
        List<Fields> tagsFields = superTableDto.getTagsFields();
        // 超级表名称
        String superTableName = superTableDto.getSuperTableName();
        // 第一个对象的字段数据类型
        DataTypeEnum dataType = ptFields.get(0).getDataType();
        // 如果该数据类型不是时间戳，打印和返回报错信息
        if (Objects.isNull(dataType) || !"timestamp".equals(dataType.getDataType())) {
            log.error("第一个字段必须为timestamp");
            return Result.fail("第一个字段必须为timestamp");
        }
        try {
            // 转码为字段Vo类对象集合
            List<FieldsVo> schemaFieldsVoList = FieldsVo.fieldsTranscoding(ptFields);
            List<FieldsVo> tagsFieldsVoList = FieldsVo.fieldsTranscoding(tagsFields);
            tdEngineService.createSuperTable(schemaFieldsVoList, tagsFieldsVoList, masterDataBaseName, superTableName);
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail(e.getMessage());
        }
        return Result.ok();
    }

    @PostMapping("/createTb")
    @ApiOperation(value = "创建超级表的子表")
    public Result<?> createTable(@RequestBody TableDTO tableDto) {
        try {
            tableDto.setDataBaseName(masterDataBaseName);
            tdEngineService.createTable(tableDto);
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail();
        }
        return Result.ok();
    }

    @PostMapping("/delTable")
    @ApiOperation(value = "删除超级表的子表")
    public Result<?> delTable(@RequestBody TableDTO tableDto) {
        try {
            tableDto.setDataBaseName(masterDataBaseName);
            tdEngineService.delTable(tableDto);
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail();
        }
        return Result.ok();
    }

    @PostMapping("/addColumnForSuperTable")
    @ApiOperation("添加列字段")
    public Result<?> addColumnForSuperTable(@RequestBody SuperTableDTO superTableDto) {
        String superTableName = superTableDto.getSuperTableName();
        if (StringUtils.isBlank(superTableName)) {
            return Result.fail("超级表名不能为空");
        }
        Fields fields = superTableDto.getFields();
        if (Objects.isNull(fields)) {
            return Result.fail("字段名不能为空");
        }
        try {
            // 列名是否已经存在
            List<DescribeDTO> column = tdEngineService.getColumn(masterDataBaseName, superTableName);
            if (CollUtil.isNotEmpty(column) && column.stream().anyMatch(c -> c.getField().equals(fields.getFieldName()))) {
                return Result.ok();
            }
            FieldsVo fieldsVo = FieldsVo.fieldsTranscoding(fields);
            tdEngineService.addColumnForSuperTable(superTableName, fieldsVo);
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail(e.getMessage());
        }
        return Result.ok();
    }

    @PostMapping("/dropColumnForSuperTable")
    @ApiOperation("删除列字段")
    public Result<?> dropColumnForSuperTable(@RequestBody SuperTableDTO superTableDto) {
        String superTableName = superTableDto.getSuperTableName();
        if (StringUtils.isBlank(superTableName)) {
            return Result.fail("超级表名不能为空");
        }
        Fields fields = superTableDto.getFields();
        if (fields == null) {
            return Result.fail("字段名不能为空");
        }
        try {
            // 列名是否存在
            List<DescribeDTO> column = tdEngineService.getColumn(masterDataBaseName, superTableName);
            if (CollUtil.isNotEmpty(column) && column.stream().anyMatch(c -> c.getField().equals(fields.getFieldName()))) {
                FieldsVo fieldsVo = FieldsVo.fieldsTranscoding(fields);
                tdEngineService.dropColumnForSuperTable(superTableName, fieldsVo);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail();
        }
        return Result.ok();
    }

    @PostMapping("/getDataByVariableAndTime")
    @ApiOperation(value = "根据变量和时间戳查询历史数据")
    public Result<List<VariableDataVO>> getDataByVariableAndTime(@RequestBody SelectDTO selectDto) {
        String tableName = TdEngIneConstants.TABLE_PREFIX + selectDto.getTableName();
        try {
            tdEngineService.checkTableExists(masterDataBaseName, tableName);
        } catch (Exception e) {
            return Result.ok();
        }
        try {
            // 变量模版
            VariableDTO dto = new VariableDTO();
            dto.setIds(selectDto.getVariableIds());
            Result<List<VariableDTO>> result = variableFeignService.listByIds(dto);
            if (Result.isError(result) || Result.isCollNull(result)) {
                return Result.ok();
            }
            List<VariableDTO> dtoList = result.getData();
            // 转为 address : VariableDTO
            Map<String, VariableDTO> map = dtoList.stream().collect(Collectors.toMap(VariableDTO::getAddress, Function.identity(), (o1, o2) -> o1));
            List<String> addressUrls = CollUtil.newArrayList(map.keySet());
            // 时间
            addressUrls.add(0, TdEngIneConstants.TS);
            // 数据库名称
            selectDto.setDataBaseName(masterDataBaseName);
            // 表名称
            selectDto.setTableName(tableName);
            // 字段名
            selectDto.setFieldNames(addressUrls);
            List<Map<String, Object>> mapList = tdEngineService.selectByVariableAndTime(selectDto);
            if (CollUtil.isEmpty(mapList)) {
                return Result.ok();
            }
            List<VariableVO> voList = transformMapToList(mapList, map, null);
            Map<String, List<VariableVO>> collect = voList.stream().collect(Collectors.groupingBy(VariableVO::getVariableId));
            List<VariableDataVO> resultVos = collect.values().stream()
                    .map(variableVos -> new VariableDataVO(variableVos.get(0).getVariableId(), variableVos))
                    .collect(Collectors.toList());
            return Result.data(resultVos);
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.data(CollUtil.newArrayList());
        }
    }

    @PostMapping("/getDataByDeviceIdAndTime")
    @ApiOperation(value = "根据设备id和时间戳查询历史数据")
    public PageResult<VariableDataVO> getDataByDeviceIdAndTime(@RequestBody BasePageQuery<SelectDTO> pageParam) {
        SelectDTO selectDto = pageParam.getParam();
        Assert.notNull(selectDto, "查询参数不能为空");
        Assert.notEmpty(selectDto.getTableName(), "表名不能为空");
        Assert.notNull(selectDto.getStartTime(), "开始时间不能为空");
        Assert.notNull(selectDto.getEndTime(), "结束时间不能为空");
        String tableName = TdEngIneConstants.TABLE_PREFIX + selectDto.getTableName();
        try {
            tdEngineService.checkTableExists(masterDataBaseName, tableName);
        } catch (Exception e) {
            return PageResult.success(CollUtil.newArrayList(), 0L);
        }
        try {
            // 变量模版
            Result<List<VariableDTO>> result = variableFeignService.listByDeviceId(Long.parseLong(selectDto.getTableName()));
            if (Result.isError(result) || Result.isCollNull(result)) {
                return PageResult.success(CollUtil.newArrayList(), 0L);
            }
            List<VariableDTO> dtoList = result.getData();
            // 转为 address : VariableDTO
            Map<String, VariableDTO> map = dtoList.stream().collect(Collectors.toMap(VariableDTO::getAddress, Function.identity(), (o1, o2) -> o1));
            List<String> addressUrls = CollUtil.newArrayList(map.keySet());
            // 时间
            addressUrls.add(0, TdEngIneConstants.TS);
            // 数据库名称
            selectDto.setDataBaseName(masterDataBaseName);
            // 表名称
            selectDto.setTableName(tableName);
            // 字段名
            selectDto.setFieldNames(addressUrls);
            // 查询总数
            Long count = tdEngineService.getCountByTimesTamp(selectDto);
            List<Map<String, Object>> mapList = tdEngineService
                    .pageByVariableAndTime(pageParam.getPageNum(), pageParam.getPageSize(), selectDto);
            if (CollUtil.isEmpty(mapList)) {
                return PageResult.success(CollUtil.newArrayList(), 0L);
            }
            List<VariableVO> voList = transformMapToList(mapList, map, null);
            Map<Date, List<VariableVO>> collect = voList.stream().collect(Collectors.groupingBy(VariableVO::getTs));
            List<VariableDataVO> resultVos = collect.values().stream()
                    .map(variableVos -> new VariableDataVO(variableVos.get(0).getTs(), variableVos))
                    .collect(Collectors.toList());
            return PageResult.success(resultVos, count);
        } catch (Exception e) {
            log.error(e.getMessage());
            return PageResult.success(CollUtil.newArrayList(), 0L);
        }
    }

    @PostMapping("/delDataByTime")
    @Log(title = "历史数据", businessType = BusinessType.DELETE)
    @ApiOperation(value = "根据时间戳删除历史数据")
    public Result<Boolean> delDataByTime(@RequestBody SelectDTO selectDto) {
        Assert.notEmpty(selectDto.getTableName(), "设备Id不能为空");
        Assert.notEmpty(selectDto.getStartTime(), "开始时间不能为空");
        Assert.notEmpty(selectDto.getEndTime(), "结束时间不能为空");
        String tableName = TdEngIneConstants.TABLE_PREFIX + selectDto.getTableName();
        Result<DeviceDTO> deviceDTOResult = deviceFeignService.selectById(Long.parseLong(selectDto.getTableName()));
        if (Result.isError(deviceDTOResult) || Result.isNull(deviceDTOResult)) {
            return Result.fail("设备信息有误");
        }
        DeviceDTO data = deviceDTOResult.getData();
        try {
            tdEngineService.checkTableExists(masterDataBaseName, tableName);
        } catch (Exception e) {
            return Result.ok();
        }
        try {
            // 数据库名称
            selectDto.setDataBaseName(masterDataBaseName);
            // 表名称
            selectDto.setTableName(tableName);
            tdEngineService.delDataByTime(selectDto);
            return Result.ok2Log(LogConstants.DELETE +
                    data.getDeviceName() + "历史数据，时间范围：" + selectDto.getStartTime() + "|" + selectDto.getEndTime());
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail("删除失败");
        }
    }

    @PostMapping("/getDataByTimestamp")
    @ApiOperation(value = "根据时间戳查询历史数据")
    @Deprecated
    public Result<List<Map<String, Object>>> getDataByTimestamp(@RequestBody SelectDTO selectDto) {
        try {
            selectDto.setDataBaseName(masterDataBaseName);
            selectDto.setTableName(TdEngIneConstants.TABLE_PREFIX + selectDto.getTableName());
            return Result.data(tdEngineService.selectByTimesTamp(selectDto));
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail(e.getMessage());
        }
    }

    @PostMapping("/getLastData")
    @ApiOperation(value = "查询最新数据")
    public Result<LastDataVO> getLastData(@RequestBody SelectDTO selectDto) {
        try {
            List<VariableDTO> dataList = redisService.getCacheObject(CacheConstants.DEVICE_REAL_DATA_KEY + selectDto.getTableName());
            if (CollUtil.isEmpty(dataList)) {
                return Result.ok();
            }
            // 重新构建前端需要格式
            Optional<VariableDTO> first = dataList.stream().findFirst();
            if (!first.isPresent()) {
                return Result.ok();
            }
            VariableDTO dto = first.get();
            Date ts = dto.getCreatedAt();
            return Result.data(new LastDataVO(ts, dataList));
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.ok();
        }
    }

    @PostMapping("/getLastDataByDeviceIds")
    @ApiOperation(value = "查询设备IDS最新数据")
    public Result<Map<String, LastDataVO>> getLastDataByDeviceIds(@RequestBody SelectDTO selectDto) {
        List<Long> deviceIds = selectDto.getDeviceIds();
        if (CollUtil.isEmpty(deviceIds)) {
            return Result.fail("设备IDS不能为空");
        }
        Map<String, LastDataVO> map = MapUtil.newHashMap();
        deviceIds.forEach(it -> {
            List<VariableDTO> dataList = redisService.getCacheObject(CacheConstants.DEVICE_REAL_DATA_KEY + it);
            if (CollUtil.isEmpty(dataList) || Objects.isNull(dataList.get(0)) || Objects.isNull(dataList.get(0).getCreatedAt())) {
                map.put(it.toString(), null);
                return;
            }
            String ts = DateUtil.formatDateTime(dataList.get(0).getCreatedAt());
            List<VariableVO> voList = CollUtil.newArrayList();
            dataList.forEach(dto -> {
                VariableVO vo = new VariableVO();
                vo.setAddress(dto.getAddress());
                vo.setValue(dto.getValue());
                vo.setDeviceId(dto.getDeviceId());
                vo.setName(dto.getName());
                vo.setType(dto.getType());
                vo.setUnit(dto.getUnit());
                vo.setTs(dto.getCreatedAt());
                voList.add(vo);
            });
            map.put(it.toString(), new LastDataVO(it, ts, voList));
        });
        return Result.data(map);
    }

    @PostMapping("/getLastDataByTags")
    @ApiOperation(value = "查询包含Tags的最新数据集合")
    public Result<Map<String, Map<String, Object>>> getLastDataByTags(@RequestBody TagsSelectDTO tagsSelectDTO) {
        try {
            tagsSelectDTO.setDataBaseName(masterDataBaseName);
            tagsSelectDTO.setStableName(TdEngIneConstants.TABLE_PREFIX + tagsSelectDTO.getStableName());
            return Result.data(tdEngineService.getLastDataByTags(tagsSelectDTO));
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail(e.getMessage());
        }
    }

    @PostMapping("/getCustomReportData")
    @ApiOperation(value = "查询自定义报表数据")
    public Result<Map<String, List<Map<String, Object>>>> getCustomReportData(@RequestBody CustomDTO customDTO) {
        try {
            TagsSelectDTO tagsSelectDTO = new TagsSelectDTO();
            tagsSelectDTO.setDataBaseName(masterDataBaseName);
            tagsSelectDTO.setStableName(TdEngIneConstants.STABLE_PREFIX + customDTO.getTemplateId());
            // tag
            List<VariableParamDTO> variables = customDTO.getVariables();
            List<String> tagsNames = CollUtil.newArrayList();
            variables.forEach(it -> tagsNames.add(it.getUrl()));
            tagsSelectDTO.setTagsNames(tagsNames);
            // where
            List<String> deviceIds = customDTO.getDevices().stream().map(DeviceParamDTO::getDeviceId).collect(Collectors.toList());
            tagsSelectDTO.setDeviceIds(deviceIds);
            tagsSelectDTO.setStartTime(customDTO.getStartTime());
            tagsSelectDTO.setEndTime(customDTO.getEndTime());

            List<Map<String, Object>> mapList = tdEngineService.getDynamicDataByDeviceId(tagsSelectDTO);
            if (CollUtil.isEmpty(mapList)) {
                return Result.ok();
            }
            // 格式转换 <时间：[设备名：[变量名：值]]>
            Map<String, List<Map<String, Object>>> stringDateMap = mapList.stream()
                    .collect(Collectors.groupingBy(it -> DateUtil.parseDateTime(it.get(TdEngIneConstants.TS).toString())))
                    .entrySet().stream()
                    .sorted(Map.Entry.comparingByKey())
                    .collect(Collectors.toMap(
                            entry -> DateUtil.formatDateTime(entry.getKey()),
                            Map.Entry::getValue,
                            (oldValue, newValue) -> oldValue,
                            LinkedHashMap::new
                    ));
            return Result.data(stringDateMap);
        } catch (Exception e) {
            log.error(e.getMessage());
            return Result.fail(e.getMessage());
        }
    }

    @ApiOperation("导出")
    @GetMapping("/export")
    public void export(HttpServletResponse response, SelectDTO selectDto) {
        if (Objects.isNull(selectDto) || StrUtil.isEmpty(selectDto.getToken()) || StrUtil.isEmpty(selectDto.getTableName())) {
            return;
        }
        LoginUser loginUser = LoginHelper.getLoginUser(selectDto.getToken());
        if (Objects.isNull(loginUser)) {
            return;
        }
        Result<DeviceDTO> deviceDTOResult = deviceFeignService.selectById(Long.parseLong(selectDto.getTableName()));
        if (Result.isError(deviceDTOResult) || Result.isNull(deviceDTOResult)) {
            return;
        }
        DeviceDTO deviceDTO = deviceDTOResult.getData();
        String tableName = TdEngIneConstants.TABLE_PREFIX + selectDto.getTableName();
        try {
            tdEngineService.checkTableExists(masterDataBaseName, tableName);
        } catch (Exception e) {
            return;
        }
        try {
            // 变量模版
            VariableDTO dto = new VariableDTO();
            dto.setIds(selectDto.getVariableIds());
            Result<List<VariableDTO>> result = variableFeignService.listByIds(dto);
            if (Result.isError(result) || Result.isCollNull(result)) {
                return;
            }
            List<VariableDTO> dtoList = result.getData();
            // 转为 address : VariableDTO
            Map<String, VariableDTO> map = dtoList.stream().collect(Collectors.toMap(VariableDTO::getAddress, Function.identity(), (o1, o2) -> o1));
            List<String> addressUrls = CollUtil.newArrayList(map.keySet());
            // 时间
            addressUrls.add(0, TdEngIneConstants.TS);
            // 数据库名称
            selectDto.setDataBaseName(masterDataBaseName);
            // 表名称
            selectDto.setTableName(tableName);
            // 字段名
            selectDto.setFieldNames(addressUrls);
            List<Map<String, Object>> mapList = tdEngineService.selectByVariableAndTime(selectDto);
            List<VariableVO> voList = CollUtil.newArrayList();
            if (CollUtil.isNotEmpty(mapList)) {
                voList = transformMapToList(mapList, map, deviceDTO.getDeviceName());
            }
            ExcelUtil.exportExcel(voList, "数据", "历史数据", VariableVO.class, response);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    @ApiOperation("导出-动态表头")
    @GetMapping("/dynamicHeadExport")
    public void dynamicHeadExport(HttpServletResponse response, SelectDTO selectDto) {
        if (Objects.isNull(selectDto) || StrUtil.isEmpty(selectDto.getToken()) || StrUtil.isEmpty(selectDto.getTableName())) {
            return;
        }
        LoginUser loginUser = LoginHelper.getLoginUser(selectDto.getToken());
        if (Objects.isNull(loginUser)) {
            return;
        }
        Result<DeviceDTO> deviceDTOResult = deviceFeignService.selectById(Long.parseLong(selectDto.getTableName()));
        if (Result.isError(deviceDTOResult) || Result.isNull(deviceDTOResult)) {
            return;
        }
        DeviceDTO deviceDTO = deviceDTOResult.getData();
        String tableName = TdEngIneConstants.TABLE_PREFIX + selectDto.getTableName();
        try {
            tdEngineService.checkTableExists(masterDataBaseName, tableName);
        } catch (Exception e) {
            return;
        }
        try {
            // 变量模版
            VariableDTO dto = new VariableDTO();
            dto.setIds(selectDto.getVariableIds());
            Result<List<VariableDTO>> result = variableFeignService.listByIds(dto);
            if (Result.isError(result) || Result.isCollNull(result)) {
                return;
            }
            List<VariableDTO> dtoList = result.getData();
            // 转为 address : VariableDTO
            Map<String, VariableDTO> map = dtoList.stream().collect(Collectors.toMap(VariableDTO::getAddress, Function.identity(), (o1, o2) -> o1));
            List<String> addressUrls = CollUtil.newArrayList(map.keySet());
            // 时间
            addressUrls.add(0, TdEngIneConstants.TS);
            // 数据库名称
            selectDto.setDataBaseName(masterDataBaseName);
            // 表名称
            selectDto.setTableName(tableName);
            // 字段名
            selectDto.setFieldNames(addressUrls);
            List<Map<String, Object>> mapList = tdEngineService.selectByVariableAndTime(selectDto);
            List<List<String>> headList = CollUtil.newArrayList();
            List<List<String>> dataList = CollUtil.newArrayList();
            if (CollUtil.isNotEmpty(mapList)) {
                List<VariableVO> voList = transformMapToList(mapList, map, deviceDTO.getDeviceName());
                // 格式转换 <时间：[变量]>
                Map<Date, List<VariableVO>> collect = voList.stream()
                        .collect(Collectors.groupingBy(VariableVO::getTs, LinkedHashMap::new, Collectors.toList()));
                // 表头
                Date date = collect.keySet().iterator().next();
                headList.add(CollUtil.newArrayList("序号"));
                headList.add(CollUtil.newArrayList("采集时间"));
                collect.get(date).forEach(it -> {
                    List<String> head = CollUtil.newArrayList();
                    String name;
                    if (StrUtil.isEmpty(it.getUnit())){
                        name = it.getName();
                    }else {
                        name = it.getName() + "(" + it.getUnit() + ")";
                    }
                    head.add(name);
                    headList.add(head);
                });
                Set<Date> dates = collect.keySet();
                AtomicInteger index = new AtomicInteger(1);
                dates.forEach(it -> {
                    List<String> data = CollUtil.newArrayList();
                    List<VariableVO> variableVOS = collect.get(it);
                    // 序号
                    data.add(String.valueOf(index.getAndIncrement()));
                    // 采集时间
                    VariableVO dataVO = variableVOS.get(0);
                    data.add(DateUtil.format(dataVO.getTs(), "yyyy-MM-dd HH:mm:ss"));
                    // 数据
                    variableVOS.forEach(variableVO -> data.add(variableVO.getValue()));
                    dataList.add(data);
                });
            }
            ExcelUtil.exportDynamicHeadExcel(headList, dataList, "数据", "历史数据", response);
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    /**
     * 历史数据封装转换
     *
     * @param mapList    历史数据
     * @param map        变量Map
     * @param deviceName 设备名称
     * @return 转换后的集合
     */
    private List<VariableVO> transformMapToList(List<Map<String, Object>> mapList, Map<String, VariableDTO> map, String deviceName) {
        List<VariableVO> voList = CollUtil.newArrayList();
        AtomicInteger index = new AtomicInteger(1);
        mapList.forEach(it -> {
            Set<String> set = it.keySet();
            String ts = it.get(TdEngIneConstants.TS).toString();
            for (String key : set) {
                if (key.equals(TdEngIneConstants.TS)) {
                    continue;
                }
                if (Objects.isNull(it.get(key))) {
                    continue;
                }
                // 处理DB5.DBD0类似这种字段出现的转换问题
                String oldKey = key;
                if (it.get(key) instanceof Map) {
                    Map<String, Object> childMap = (Map<String, Object>) it.get(key);
                    Set<String> childSet = childMap.keySet();
                    for (String childKey : childSet) {
                        key = String.format("%s.%s", key, childKey);
                        String childValue = (String) childMap.get(childKey);
                        if (StrUtil.isEmpty(childValue)) {
                            childValue = "NAN";
                        }
                        VariableDTO var = map.get(key);
                        VariableVO vo = VariableVO.builder()
                                .address(key)
                                .value(childValue)
                                .deviceId(var.getDeviceId())
                                .name(var.getName())
                                .type(var.getType())
                                .unit(var.getUnit())
                                .indicatesStatus(var.getIndicatesStatus())
                                .variableId(var.getId().toString())
                                .ts(DateUtil.parseDateTime(ts))
                                .deviceName(deviceName)
                                .index(index.getAndIncrement())
                                .build();
                        voList.add(vo);
                        // 重置key
                        key = oldKey;
                    }
                }else {
                    VariableDTO var = map.get(key);
                    VariableVO vo = VariableVO.builder()
                            .address(key)
                            .value(it.get(key).toString())
                            .deviceId(var.getDeviceId())
                            .name(var.getName())
                            .type(var.getType())
                            .unit(var.getUnit())
                            .indicatesStatus(var.getIndicatesStatus())
                            .variableId(var.getId().toString())
                            .ts(DateUtil.parseDateTime(ts))
                            .deviceName(deviceName)
                            .index(index.getAndIncrement())
                            .build();
                    voList.add(vo);
                }
            }
        });
        return voList;
    }
}
